package com.vanke.agent.sync;

import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import net.sf.json.JSONObject;

import org.apache.log4j.Logger;
import org.apache.thrift.TException;
import org.wang.hongyan.common.lock.LockContext;

import com.common.api.handler.MappingHandler;
import com.vanke.agent.ADataSource;
import com.vanke.agent.C;
import com.vanke.agent.Cfg;
import com.vanke.agent.bean.CstInfo;
import com.vanke.agent.bean.FollowRecord;
import com.vanke.agent.bean.Mapping;
import com.vanke.agent.common.Cache;
import com.vanke.agent.common.CardID;
import com.vanke.agent.common.CstUtil;
import com.vanke.agent.common.FollowRecordUtil;
import com.vanke.agent.common.GUIDCacheChecker;
import com.vanke.agent.common.QueueMapMessage;
import com.vanke.agent.common.Util;
import com.vanke.agent.common.log.LoggerUtil;
import com.vanke.agent.common.thrift.UniteSericeClient;
import com.vanke.agent.tools.merge.Merge;

public class CustomerSync extends AgentSync {
	protected Logger log = Logger.getLogger(CustomerSync.class);
	protected Logger bizLog = LoggerUtil.getLoggerByName("customer_sync");
	protected Logger cstNoticeLog = LoggerUtil.getLoggerByName("cst_notice");
	protected UniteSericeClient client;
	protected Set<Long> syncedGuid = new HashSet<Long>();

	public CustomerSync() {
		connectSeriesService();
	}
	
	private void connectSeriesService() {
		String addr = Cfg.get("serial.service.addr");
		try {
			int index = addr.indexOf(':');
			String port = addr.substring(index + 1);
			addr = addr.substring(0, index);
			client = UniteSericeClient.getClient(addr, Integer.parseInt(port));
			log.info("[10]SYNC series server: " + addr + ":" + port);
		} catch (Exception e) {
			e.printStackTrace();
			client = null;
			bizLog.error(e.getMessage(), e);
			//AgentContext.exit("[10]无法连接序列号服务[" + addr + "]");
		}
	}

	@Override
	public void sync() {
		try {
			GUIDCacheChecker.loadGuidCache(GUIDCacheChecker.ETYPE_CST);
			int errors = 0;
			for (int i = 0; i < 10; i++) {
				if (!syncCreate()) {
					break;
				}
				errors++;
			}
			if (errors == 10) {
				pause = true;
				return;
				// AgentContext.exit("Fatal ERROR:连续10次尝试按创建时间同步客户失败");
			}
			errors = 0;
			for (int i = 0; i < 10; i++) {
				if (!syncModify()) {
					break;
				}
				errors++;
			}
			if (errors == 10) {
				pause = true;
				return;
				// AgentContext.exit("Fatal ERROR:连续10次尝试按修改时间同步客户失败");
			}
			pause = false;
		} catch (Exception e) {
			e.printStackTrace();
			bizLog.error("[10]sync customer error: " + e);
		} finally {
			GUIDCacheChecker.release(GUIDCacheChecker.ETYPE_CST);
		}
		updateSyncedCustomerMapping();
		syncedGuid.clear();
	} //~sync

	/*
	 * 理亲本次同步客户的映射列表
	 */
	protected void updateSyncedCustomerMapping() {
		if (syncedGuid.size() == 0) {
			return;
		}
		bizLog.info("更新已同步客户映射");
		MappingHandler mappingHandler = new MappingHandler();
		Connection bizCon = ADataSource.getConnection(ADataSource.SCHEMA_BIZ);
		try {
			//-------------------更新客户映射列表----------------------------
			Map<String, Map<Long, Mapping>> mappings = Cache.loadCache(bizCon,
					syncedGuid);
			Iterator<Entry<String, Map<Long, Mapping>>> iter = mappings
					.entrySet().iterator();
			while (iter.hasNext()) {
				Entry<String, Map<Long, Mapping>> entry = iter.next();
				Map<Long, Mapping> map = entry.getValue();
				if (mappingHandler.updateMapping(bizCon, entry.getKey(), map)) {
					bizLog.info("更新成功" + map.keySet());
				} else {
					bizLog.info("更新失败" + map.keySet());
				}
			} // end while

			//-------------------客户资料修改通知----------------------------
			iter = mappings.entrySet().iterator();
			while (iter.hasNext()) {
				Entry<String, Map<Long, Mapping>> entry = iter.next();
				noticeCstInfoUpdate(entry.getValue());
			} // end while
		} catch (Exception e) {
			e.printStackTrace();
			log.error(Util.formatStackTrace(e));
		} finally {
			ADataSource.close(bizCon);
		}
	} //~updateSyncedCustomerMapping

	/*
	 * 资料更新通知
	 */
	protected void noticeCstInfoUpdate(Map<Long, Mapping> map) {
		Connection bizCon = ADataSource.getConnection(ADataSource.SCHEMA_BIZ);
		Connection myCon = ADataSource.getConnection(ADataSource.SCHEMA_MY);

		// 加载客户项目映射
		Map<String, String> cstProjMapping = loadCstProjMapping(myCon, map);

		try {
			Iterator<Entry<Long, Mapping>> iter = map.entrySet().iterator();
			while (iter.hasNext()) {
				Entry<Long, Mapping> entry = iter.next();
				Mapping mapping = entry.getValue();
				Long cstGUID = entry.getKey();
				// 遍历一个客户对应明源GUID列表
				for (String oriGUID : mapping.getOriGuids()) {
					String projGUID = cstProjMapping.get(oriGUID);
					if (projGUID == null) { // 客户没项目属性，不通知
						continue;
					}
					String projCode = getProjectCode(myCon, projGUID);
					// TODO 判断是有案场跟进，hasAcFollow
					// 从数据中心取客户资料，针对每一个人最一次，会有很大的性能消耗
					// 因每日最多一万，平均每小时最多两千，因此可在一小时窗口完成，确保信息一致
					CstInfo c = CstUtil.getByGuid(bizCon, cstGUID);
					if (c == null) {
						// 一般不会出现，即未找到客户；
						// 在正要取客户资料，web端执行合并就会出现
						continue;
					}
					sendNotice(c, mapping, projCode);
				} // end for
			} // end while
		} catch (Exception e) {
			log.error(Util.formatStackTrace(e));
		} finally {
			ADataSource.close(bizCon);
			ADataSource.close(myCon);
		}
	} //~noticeCstInfoUpdate
	
	protected boolean hasAcFollow(Connection bizCon, long cstGUID, String projCode) {
		String table = CstUtil.retrieveCstReceiveTable(cstGUID);
		String sql = "select * from " + table
		        + " where SrcType like 'ac_%' and projCode=? limit 1";
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		try {
			pstmt = bizCon.prepareStatement(sql);
			pstmt.setString(1, projCode);
			rs = pstmt.executeQuery();
			return rs.next();
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		} finally {
			ADataSource.close(rs, pstmt, null);
		}
		return false;
	}

	protected void sendNotice(CstInfo c, Mapping mapping, String projCode) {
		//		cstGUID	是	string	20	客户ID
		//		projCode	是	string	32	项目code
		//		cstName	否	string	16	客户姓名
		//		mobile	否	array		数组，手机号码列表
		//		address	否	string	64	家庭地址
		//		cardType	否	int		证件类型 1身份证
		//		cardId	否	string	18	证件号码
		try {
			JSONObject json = new JSONObject();
			json.put("cstGUID", c.getCstGUID());
			json.put("projCode", projCode);
			json.put("cstName", c.getCstName());
			json.put("mobile", mapping.getMobiles());
			json.put("address", c.getAddress());
			json.put("cardType", c.getCardType());
			Iterator<String> iter = mapping.getCardIds().iterator();
			json.put("cardId", iter.hasNext() ? iter.next() : "");

			cstNoticeLog.info(json); // 记日志

			QueueMapMessage ms = new QueueMapMessage();
			ms.setMessageType(C.NOTICE_CST_UPDATE);
			ms.setObj(json.toString());
			ms.send();
		} catch (Exception e) {
			log.error("send cst notice error:" + Util.formatStackTrace(e));
		}
	} //~sendNotice

	/*
	 * 加载客户项目映射
	 */
	protected Map<String, String> loadCstProjMapping(Connection myCon,
			Map<Long, Mapping> map) {
		Map<String, String> cstProjMapping = new HashMap<String, String>();
		// 从明源取明源GUID与项目映射列表
		Set<String> oriGuids = new HashSet<String>();
		Iterator<Entry<Long, Mapping>> iter = map.entrySet().iterator();
		while (iter.hasNext()) {
			Entry<Long, Mapping> entry = iter.next();
			Mapping mapping = entry.getValue();
			oriGuids.addAll(mapping.getOriGuids());
			if (oriGuids.size() >= 5000) { // 为免sql超长，最多5000取一次
				cstProjMapping.putAll(doLoadCstProjMapping(myCon, oriGuids));
				oriGuids.clear();
			} // end if
		} // end while
		if (oriGuids.size() > 0) { // 不是5000的倍数
			cstProjMapping.putAll(doLoadCstProjMapping(myCon, oriGuids));
		}
		return cstProjMapping;
	} //~loadCstProjMapping

	/*
	 * 加载客户对应项目映射
	 */
	protected Map<String, String> doLoadCstProjMapping(Connection con,
			Set<String> oriGuids) {
		Map<String, String> mapping = new HashMap<String, String>();
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		String sql = "select cstguid,projguid from p_CstAttach "
				+ " where projguid is not null and cstguid in("
				+ Util.convertToInSQL(oriGuids.toString()) + ")";
		try {
			pstmt = con.prepareStatement(sql);
			rs = pstmt.executeQuery();
			while (rs.next()) {
				mapping.put(rs.getString(1), rs.getString(2));
			}
		} catch (Exception e) {
			log.error(Util.formatStackTrace(e));
		} finally {
			ADataSource.close(rs, pstmt, null);
		}
		return mapping;
	} //~loadCstProjMapping

	/*
	 * 按修改时间同步客户，返回是否需要重新同步；
	 * 如果正常同步完成，则返回false，任何异常，则返回true
	 */
	public boolean syncCreate() {
		bizLog.info("[10]SYNC 按创建时间同步客户主档");
		Connection myCon = ADataSource.getConnection(ADataSource.SCHEMA_MY);
		Connection bizCon = ADataSource.getConnection(ADataSource.SCHEMA_BIZ);

		try {
			bizCon.setAutoCommit(false);
			String timeStr = dateFormat.format(Util.offMinute(
					customerCfg.beginTime, rebackOffset));

			String querySql = "SELECT " + C.FIELDS_CST + " FROM p_Customer "
					+ " WHERE ModifyOn is null AND CreatedOn >=? "
					+ " AND CreatedOn < ? order by CreatedOn asc";

			PreparedStatement pstmt = myCon.prepareStatement(querySql,
					ResultSet.TYPE_SCROLL_INSENSITIVE,
					ResultSet.CONCUR_READ_ONLY);
			pstmt.setString(1, timeStr);
			Timestamp t = new Timestamp(System.currentTimeMillis());
			pstmt.setTimestamp(2, t);
			bizLog.info("[10]SYNC_SQL " + querySql + "[" + timeStr + "~"
					+ dateFormat.format(t) + "]");

			ResultSet rs = pstmt.executeQuery();

			int count = 0, record = 0;
			while (rs.next()) {
				record++;
				CstInfo c = CstUtil.pickCstInfo(rs);
				Timestamp modify = c.getLastModifyTime();
				String guid = c.getOriGUID();
				int etype = GUIDCacheChecker.ETYPE_CST;
				int check = GUIDCacheChecker.existsGUID(guid, modify, etype);
				switch (check) {
				case GUIDCacheChecker.CHECK_SAME:
					bizLog.info("[10]SYNC_IGNORE 已处理客户，忽略不处理[" + c + "]");
					continue;
				case GUIDCacheChecker.CHECK_EXIST:
					GUIDCacheChecker.updateGUID(guid, modify, etype);
					break;
				case GUIDCacheChecker.CHECK_NOT_EXIST:
					GUIDCacheChecker.saveGUID(guid, modify, etype);
					break;
				}
				try {
					syncCstInfo(c, bizCon);
					bizCon.commit();
					count++;
				} catch (Exception e) {
					e.printStackTrace();
					bizLog.error("[10]SYNC_ERR 同步出错[" + c + "]:" + e);
					bizCon.rollback();
				}
				customerCfg.beginTime = new Date(rs.getTimestamp("CreatedOn")
						.getTime());
			}
			// 保存配置，最后一次的时间
			Cfg.set("import.customer.created.time",
					dateFormat.format(customerCfg.beginTime));
			Cfg.save();

			// 关闭
			ADataSource.close(rs);
			ADataSource.close(pstmt);
			//ADataSource.close(wpstmt);

			bizLog.info("[10]SYNC " + count + " new customer synced by "
					+ record + " records");
			bizLog.info("[10]SYNC_END(create)");
			return false;
		} catch (Exception e) {
			e.printStackTrace();
			bizLog.error("[10]SYNC_ERR sync new customer error: " + e);
			return true;
		} finally {
			ADataSource.close(myCon);
			ADataSource.close(bizCon);
		}
	}

	/*
	 * 按创建时间同步客户，返回是否需要重新同步；
	 * 如果正常同步完成，则返回false，任何异常，则返回true
	 */
	public boolean syncModify() {
		bizLog.info("[10]SYNC 按修改时间同步客户主档");
		Connection myCon = ADataSource.getConnection(ADataSource.SCHEMA_MY);
		Connection bizCon = ADataSource.getConnection(ADataSource.SCHEMA_BIZ);

		try {
			bizCon.setAutoCommit(false);
			String timeStr = dateFormat.format(Util.offMinute(
					customerCfg.modifyTime, rebackOffset));

			String querySql = "SELECT " + C.FIELDS_CST + " FROM p_Customer "
					+ " WHERE ModifyOn is not null AND ModifyOn >= ? "
					+ " AND ModifyOn < ? order by ModifyOn asc";
			//			String querySql = "SELECT "
			//					+ C.FIELDS_CST
			//					+ " from p_Customer where CstGUID='08029C31-9881-447B-AACA-2A5C1E2EDC45'";

			PreparedStatement pstmt = myCon.prepareStatement(querySql,
					ResultSet.TYPE_SCROLL_INSENSITIVE,
					ResultSet.CONCUR_READ_ONLY);
			pstmt.setString(1, timeStr);
			Timestamp t = new Timestamp(System.currentTimeMillis());
			pstmt.setTimestamp(2, t);
			bizLog.info("[10]SYNC_SQL " + querySql + "[" + timeStr + "~"
					+ dateFormat.format(t) + "]");

			ResultSet rs = pstmt.executeQuery();
			int count = 0, record = 0;
			while (rs.next()) {
				record++;
				CstInfo c = CstUtil.pickCstInfo(rs);
				Timestamp modify = rs.getTimestamp("ModifyOn");
				String guid = c.getOriGUID();
				int etype = GUIDCacheChecker.ETYPE_CST;
				int check = GUIDCacheChecker.existsGUID(guid, modify, etype);
				switch (check) {
				case GUIDCacheChecker.CHECK_SAME:
					bizLog.info("SYNC_IGNORE[01]已处理客户，忽略不处理[" + c + "]");
					continue;
				case GUIDCacheChecker.CHECK_EXIST:
					GUIDCacheChecker.updateGUID(guid, modify, etype);
					break;
				case GUIDCacheChecker.CHECK_NOT_EXIST:
					GUIDCacheChecker.saveGUID(guid, modify, etype);
					break;
				}
				try {
					syncCstInfo(c, bizCon);
					bizCon.commit();
					count++;
				} catch (Exception e) {
					bizLog.error("[10]SYNC_ERR 同步出错[" + c + "]:" + e);
					bizCon.rollback();
				}
				customerCfg.modifyTime = new Date(modify.getTime());
			}
			// 保存配置，最后一次的时间
			Cfg.set("import.customer.modify.time",
					dateFormat.format(customerCfg.modifyTime));
			Cfg.save();

			// 关闭
			ADataSource.close(rs);
			ADataSource.close(pstmt);

			bizLog.info("[10]SYNC " + count + " modify customer synced by "
					+ record + " records");
			bizLog.info("[10]SYNC_END (modify)");
			return false;
		} catch (Exception e) {
			e.printStackTrace();
			bizLog.error("[10]SYNC_ERR sync modify customer error: " + e);
			return true;
		} finally {
			ADataSource.close(myCon);
			ADataSource.close(bizCon);
		}
	} //~syncModify

	protected String formatLog(CstInfo c, CstInfo old) {
		return "\nMY[" + c + "]\nDC[" + old + "]";
	}

	/*
	 * 明源GUID、身份证、手机，组合情况
	 * 1. 明源GUID
	 */
	public void syncCstInfo(CstInfo c, Connection con) throws Exception {
		bizLog.info("处理客户同步[" + c + "]");

		/*--------------------------------------------------------------*/
		CstInfo oldCi = getByOriGuid(con, c.getOriGUID());
		if (oldCi != null) { // 如果按明源GUID找到了，即认为是同一个人
			handleGUIDSame(con, c, oldCi);
			syncedGuid.add(c.getCstGUID());
			return;
		} // end if (oldCi != null)
		/*--------------------------------------------------------------*/
		if (CardID.valid(c.getCardId())) {// 有身份证
			handleCardIdValid(con, c);
			syncedGuid.add(c.getCstGUID());
			return;
		} // end if (CardID.valid(c.getCardId()))
		/*--------------------------------------------------------------*/
		if (c.getMobiles().size() > 0) { // 如果手机列表不空，表明有有效手机
			handleMobileValid(con, c);
			syncedGuid.add(c.getCstGUID());
			return;
		} // end if (c.getMobiles().size() > 0)
		/*--------------------------------------------------------------*/

		bizLog.info("[-1]无效记录，忽略处理[" + c + "]");
	} //~syncCstInfo

	/*
	 * 明源GUID相同
	 */
	protected void handleGUIDSame(Connection con, CstInfo c, CstInfo oldCi)
			throws SQLException {
		bizLog.info("[101]明源GUID映射存在");
		String cCardId = c.getCardId();
		boolean cValid = CardID.valid(cCardId);

		c.setCstGUID(oldCi.getCstGUID());
		bizLog.info("[101]更新主档[" + formatLog(c, oldCi) + "]");
		updateCstInfo(con, c, oldCi); // 更新主档
		bizLog.info("[101]更新完成" + formatLog(c, oldCi));
		if (cValid) {
			// 根据身份证查找GUID映射
			long guid = CstUtil.getCstGUIDByCardid(con, cCardId);
			if (guid == 0) { // 身份证映射不存在，则添加
				bizLog.info("[101]添加身份证映射[" + cCardId + "-" + c.getCstGUID()
						+ "]");
				saveCardMapping(con, c);  // 添加身份证映射
			} else {
				if (guid != c.getCstGUID()) { // 有身份证，ID与当前GUID查到的不一致
					// 执行合并
					con.commit();
					bizLog.info("[101]合并主档[" + guid + "->" + c.getCstGUID()
							+ "]");
					long mergeGuid = merge(con, "[101]", guid, c.getCstGUID());
					c.setCstGUID(mergeGuid); // 此时GUID为合并保留的GUID
					//					c.getMobiles().addAll(tempCache.getCstMobiles(guid));
				}
			} // end if
		} // end if (cValid)
		bizLog.info("[101]更新处理后[" + c + "]");
		handleMobiles(con, c, "[101]");
		bizLog.info("[101]处理完成[" + c + "]");
	} //~handleGUIDSame(Connection con, CstInfo c, CstInfo oldCi)

	/*
	 * 处理身份证有效
	 */
	protected void handleCardIdValid(Connection con, CstInfo c)
			throws SQLException {
		bizLog.info("[102]明源GUID映射不存在，身份证有效");
		CstInfo old = getByCardid(con, c.getCardId());// 根据身份证查询已有库；
		if (old != null) {
			handleCardIdValidAndExists(con, c, old);
		} else {//查不到身份证，表示新用户
			handleCardIdValidAndNotExists(con, c);
		}
	} //~handleCardIdValid(Connection con, CstInfo c)

	/*
	 * 身份证有效，且数据中心已存在
	 */
	protected void handleCardIdValidAndExists(Connection con, CstInfo c,
			CstInfo old) throws SQLException {
		bizLog.info("[103]身份证有效，且数据中心已存在");
		c.setCstGUID(old.getCstGUID());
		bizLog.info("[103]更新主档" + formatLog(c, old));
		updateCstInfo(con, c, old); // 更新主档
		if (getByOriGuid(con, c.getOriGUID()) == null) {
			saveGUIDMapping(con, c);
		}
		bizLog.info("[103]更新完成" + formatLog(c, old));

		bizLog.info("[103]更新处理后[" + c + "]");
		handleMobiles(con, c, "[103]");
		bizLog.info("[103]处理完成[" + c + "]");
	} //~handleCardIdValidAndExists

	/*
	 * 身份证有效，且数据中心不存在
	 */
	protected void handleCardIdValidAndNotExists(Connection con, CstInfo c)
			throws SQLException {
		bizLog.info("[104]身份证有效，且数据中心不存在");
		bizLog.info("[104]新增主档[" + c + "]");
		saveCstInfo(con, c, false);
		bizLog.info("[104][" + c + "]");

		bizLog.info("[104]新增处理后[" + c + "]");
		handleMobiles(con, c, "[104]");
		bizLog.info("[104]处理完成[" + c + "]");
	} //~handleCardIdValidAndNotExists

	/*
	 * 手机有效
	 */
	protected void handleMobileValid(Connection con, CstInfo c)
			throws SQLException {
		bizLog.info("[105]明源GUID映射不存在，身份证无效；存在有效手机");
		String mobile = c.getMobile();
		CstInfo old = getByMobile(con, mobile);
		if (old == null) {
			mobile = c.getHomeTel();
			if (Util.isMobile(mobile)) {
				old = getByMobile(con, mobile);
			}
		}
		if (old == null) {
			mobile = c.getOfficeTel();
			if (Util.isMobile(mobile)) {
				old = getByMobile(con, mobile);
			}
		}
		if (old == null) {
			handleMobileValidAndNotExists(con, c);
		} else {
			handleMobileValidAndExists(con, c, old, mobile);
		}
	} //~handleMobileValid

	/*
	 * 手机有效，且数据中心已存在
	 */
	protected void handleMobileValidAndExists(Connection con, CstInfo c,
			CstInfo old, String mobile) throws SQLException {
		bizLog.info("[106]手机有效，且数据中心已存在");

		if (old.getOriGUID() == null || old.getOriGUID().equals("")) {
			c.setCstGUID(old.getCstGUID());
			bizLog.info("[106]更新主档" + formatLog(c, old));
			updateCstInfo(con, c, old); // 更新主档
			if (getByOriGuid(con, c.getOriGUID()) == null) {
				saveGUIDMapping(con, c);
			}
			bizLog.info("[106]更新完成" + formatLog(c, old));
		} else {
			bizLog.info("[106]新增主档" + formatLog(c, old));
			saveCstInfo(con, c, false);
			bizLog.info("[106]新增主档完成" + formatLog(c, old));
		}

		bizLog.info("[106]更新处理后[" + c + "]");
		handleMobiles(con, c, "[106]");
		bizLog.info("[106]处理完成[" + c + "]");
	} //~handleMobileValidAndExists

	/*
	 * 手机有效，且数据中心不存在
	 */
	protected void handleMobileValidAndNotExists(Connection con, CstInfo c)
			throws SQLException {
		bizLog.info("[107]手机有效，且数据中心不存在");

		bizLog.info("[107]新增主档[" + c + "]");
		saveCstInfo(con, c, false);
		bizLog.info("[107][" + c + "]");

		bizLog.info("[107]新增处理后[" + c + "]");
		handleMobiles(con, c, "[107]");
		bizLog.info("[107]处理完成[" + c + "]");
	} //~handleMobileValidAndNotExists

	protected void handleMobiles(Connection con, CstInfo c, String code)
			throws SQLException {
		Set<String> mobiles = c.getMobiles();
		bizLog.info(code + "处理手机列表" + mobiles);
		for (String cMobile : mobiles) { // 手机列表中，均是有效手机
			// 根据手机查找GUID映射
			// long guid = CstUtil.getCstGUIDByMobile(con, cMobile);
			CstInfo old = getByMobile(con, cMobile);
			if (old == null) { // 手机映射不存在，则添加
				bizLog.info(code + "添加手机映射[" + cMobile + "]");
				saveMobileMapping(con, c, cMobile, code);  // 添加手机映射
				continue;
			}
			if (old.getCstGUID() == c.getCstGUID()) {
				continue;
			}
			bizLog.info(code + "手机映射已存在[" + old + "]");

			if (shouldMerge(code, c, old)) {
				con.commit();
				long from = c.getCstGUID(), to = old.getCstGUID();
				bizLog.info(code + "合并主档[" + from + "->" + to + "]");
				long mergeGuid = merge(con, code, from, to);
				c.setCstGUID(mergeGuid); // 此时GUID为合并保留的GUID
			} else {
				updateMobileMapping(con, cMobile, old.getCstGUID(),
						c.getCstGUID(), code);
				saveConflic(cMobile, old, c, code, code);
			}
		} // end for (String cMobile : mobiles)
	} //~handleMobiles

	/*
	 * 判断新旧主档是否需要合并，如果不需要，则记为冲突；
	 */
	public boolean shouldMerge(String code, CstInfo c, CstInfo old)
			throws SQLException {
		boolean newCvalid = CardID.valid(c.getCardId());
		boolean oldCvalid = CardID.valid(old.getCardId());
		String oldOriGuid = old.getOriGUID();
		if (oldOriGuid == null || oldOriGuid.equals("")) {
			// 如果查到的主档，明源GUID为空
			if (!oldCvalid) { // 旧档没身份证，合并
				bizLog.info(code + "旧档无明源GUID映射，且无有效身份证，执行合并");
				return true;
			}
			if (newCvalid) { // 新旧档均有身份证，则不合；保存冲突
				bizLog.info(code + "旧档无明源GUID映射，新旧档均有身份证，不合并");
				// 经过[102]流程，两身份证不可能相同
				return false;
			}
			return shouldMergeByName(code + "[1]", c, old);
		} // end if (oldOriGuid == null || oldOriGuid.equals(""))
		Calendar calNew = Calendar.getInstance();
		calNew.setTime(c.getLastModifyTime());
		Calendar calOld = Calendar.getInstance();
		calOld.setTime(old.getLastModifyTime());

		calOld.add(Calendar.MONTH, 3); // 旧档往后加3个月
		if (calOld.getTimeInMillis() < calNew.getTimeInMillis()) {
			bizLog.info(code + "新旧档更新间隔超过3个月，不合并");
			return false; // 更新相隔超过3个月
		}

		if (newCvalid) {
			if (oldCvalid) {
				bizLog.info(code + "新旧档均有有效身份证，不合并");
				return false;
			}
		}
		return shouldMergeByName(code + "[2]", c, old);
	} //~shouldMerge(CstInfo c, CstInfo old)

	/*
	 * 根据性别和姓名判断是否应该合并
	 */
	protected boolean shouldMergeByName(String code, CstInfo c, CstInfo old) {
		String newName = c.getCstName();
		String oldName = old.getCstName();
		if (newName.length() < 2 || oldName.length() < 2) {
			bizLog.info(code + "新旧档存在无效姓名，不合并");
			return false; // 如果任何一个没姓名，则不合并
		}
		char newCh = newName.charAt(0);
		char oldCh = oldName.charAt(0);
		if (newCh != oldCh) { // 姓不同（复姓取首字）
			bizLog.info(code + "新旧档姓氏不同，不合并");
			return false;
		}
		bizLog.info(code + "新旧档姓氏相同，执行合并");
		return true;
	} //~shouldMergeByNameAndSex(CstInfo c, CstInfo old)

	/*
	 * 合并主档
	 */
	public long merge(Connection con, String code, long from, long to) {
		if (from == to) {
			return to;
		}
		Merge merge = new Merge();
		// 默认取都无推荐关系
		boolean toHas = false;
		boolean fromHas = false;

		bizLog.info("[CustomerSync]尝试锁定客户[" + from + "," + to + "]");
		boolean fromLock = LockContext.tryLock(from, C.LOCK_TRY_TIMEOUT * 12);
		boolean toLock = LockContext.tryLock(to, C.LOCK_TRY_TIMEOUT * 12);
		if (!fromLock) {
			bizLog.info("客户[" + from + "]未锁定,强制执行合并");
		}
		if (!toLock) {
			bizLog.info("客户[" + to + "]未锁定,强制执行合并");
		}
		try {
			toHas = hasRecommend(con, to);
			fromHas = hasRecommend(con, from);
			bizLog.info(String.format(code + "是否有推荐关系检查[%s:%s,%s:%s]", // 
					from,					//
					fromHas ? "有" : "无", 	//
					to, 					//
					toHas ? "有" : "无"		//
			));
		} catch (Exception e) {
			e.printStackTrace();
			bizLog.error(code + "检推荐关系异常：" + e);
		}
		// 只有在from有推荐，to没有的情况下才取from；其余均取to
		List<FollowRecord> frList; 	// 被合并掉的主档的跟进记录列表（不含预约）
		long retainGuid = to;			// 合并后保留的GUID
		long dropedGuid = from;
		boolean merged = false;		// 是否合并成功
		try {
			
			if (fromHas && !toHas) {
				bizLog.info(code + "实际执行合并[" + to + "->" + from + "]");
				frList = FollowRecordUtil.loadFollowRecord(con, to, code,
						bizLog);
				merged = merge.mergeCst(to, from);
				retainGuid = from;
				dropedGuid = to;
			} else {
				bizLog.info(code + "实际执行合并[" + from + "->" + to + "]");
				frList = FollowRecordUtil.loadFollowRecord(con, from, code,
						bizLog);
				merged = merge.mergeCst(from, to);
				retainGuid = to;
				dropedGuid = from;
			}
			if (merged) {
				// bizLog.info(code + "合并成功，重推跟进通知");
				bizLog.info(code + "合并成功!");
				bizLog.info(code + "发送客户资料合并通知");
				CstUtil.sendMergeNotice(retainGuid, dropedGuid, null);
				bizLog.info(code + "重推跟进通知");
				// 如果合并成功，则将通知下发
				FollowRecordUtil.resendFollowRecords(frList, retainGuid);
			}
		} catch (Exception e) {
			e.printStackTrace();
			bizLog.error("客户同步合并异常:" + e);
		} finally {
			LockContext.unlock(from);
			LockContext.unlock(to);
		}
		return retainGuid;
	} //~merge(long from, long to)

	/*
	 * 检查是否有推荐关系
	 */
	protected boolean hasRecommend(Connection con, long guid) throws Exception {
		//	prm_Agent_Cst_Recommed_
		String table = CstUtil.calcCstSplitTable("prm_Agent_Cst_Recommed_",
				guid);
		Statement stmt = null;
		ResultSet rs = null;
		try {
			String sql = "select cstguid from " + table + " where cstguid="
					+ guid;
			stmt = con.createStatement();
			rs = stmt.executeQuery(sql);
			return rs.next();
		} catch (Exception e) {
			throw e;
		} finally {
			ADataSource.close(rs, stmt, null);
		}
	} //~hasRecommend(Connection con, long guid)

	/*
	 * 保存冲突
	 */
	protected void saveConflic(String mobile, CstInfo from, CstInfo to,
			String comment, String code) {
		bizLog.info(String.format("%s保存存手机冲突[mobile=%s,原本映射[%s]，当前[%s]", code,
				mobile, from.getOriGUID(), to.getOriGUID()));
		Connection con = ADataSource.getConnection(ADataSource.SCHEMA_BIZ);

		Map<String, Object> recomm = getRemommInfo(con, mobile, from);
		Boolean conflic = (Boolean) recomm.get("result");
		bizLog.info("判断是否有推荐关系[" + from + "][" + conflic + "]");

		PreparedStatement pstmt = null;
		Timestamp t = new Timestamp(System.currentTimeMillis());
		try {
			String field = "Mobile,CstFrom,CstFromName,CstFromCardID,CstTo,CstToName,CstToCardID,HandleStatus,CreateTime,HasRecommConflic,CstFromProjCode,CstFromProjName,Comment";
			String insertSql = CstUtil.formatInsertSql("cst_Conflic", field);
			pstmt = con.prepareStatement(insertSql);

			pstmt.setString(1, mobile); // Mobile	varchar
			pstmt.setLong(2, from.getCstGUID()); // CstFrom	bigint
			pstmt.setString(3, from.getCstName()); // CstFromName	varchar
			pstmt.setString(4, from.getCardId()); // CstFromCardID	varchar
			pstmt.setLong(5, to.getCstGUID()); // CstTo	bigint
			pstmt.setString(6, to.getCstName());// CstToName	varchar
			pstmt.setString(7, to.getCardId()); // CstToCardID	varchar
			pstmt.setInt(8, 0); // HandleStatus	int
			pstmt.setTimestamp(9, t); // CreateTime	datetime
			pstmt.setInt(10, conflic ? 1 : 0); // HasRecommConflic	int
			pstmt.setString(11, conflic ? (String) recomm.get("projCode")
					: null); // CstFromProjCode	varchar
			pstmt.setString(12, conflic ? (String) recomm.get("projName")
					: null); // CstFromProjName	varchar
			pstmt.setString(13, comment); // Comment	varchar

			pstmt.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
			bizLog.error(code + "保存冲突失败" + e);
		} finally {
			if (conflic) {
				saveMobileBlack(con, mobile, from, to, t);
			}
			ADataSource.close(null, pstmt, con);
		}
	} //~saveConflic(String mobile, long from, long to)

	/*
	 * 查找推荐冲突
	 */
	protected void saveMobileBlack(Connection con, String mobile, CstInfo from,
			CstInfo to, Timestamp t) {
		bizLog.info("添加黑名单[" + from + "]->[" + to + "]");
		PreparedStatement queryPstmt = null;
		ResultSet queryRs = null;
		try {
			String sql = "select 0 from cst_Blacklist where Mobile=? and (ProjCode is null or ProjCode='') and BlackStatus=0";
			queryPstmt = con.prepareStatement(sql);
			queryPstmt.setString(1, mobile);
			queryRs = queryPstmt.executeQuery();
			if (queryRs.next()) {
				// 如果已经存在全局的黑名单，且未处理，则不再添加
				bizLog.info("全局黑名单已存在，忽略保存Ｆ");
				return;
			}
		} catch (Exception e) {
			e.printStackTrace();
			bizLog.info("查询全局黑名单异常[" + e + "]");
		} finally {
			ADataSource.close(queryRs, queryPstmt, null);
		}
		PreparedStatement savePstmt = null;
		String comment = "同步冲突：" + from.getCstGUID() + "->" + to.getCstGUID();
		try {
			String sql = "insert into cst_Blacklist(Mobile,CstGUID,ProjCode,`Comment`,CreateTime,APPID,`Type`,BlackStatus)values(?,?,?,?,?,?,?,?)";
			savePstmt = con.prepareStatement(sql);
			savePstmt.setString(1, mobile);
			savePstmt.setInt(2, 0);
			savePstmt.setString(3, "");
			savePstmt.setString(4, comment);
			savePstmt.setTimestamp(5, t);
			savePstmt.setInt(6, 100);
			savePstmt.setInt(7, 1);
			savePstmt.setInt(8, 1);
			savePstmt.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
			bizLog.error("保存黑名单异常：" + e);
		} finally {
			ADataSource.close(savePstmt);
		}
	} //~saveMobileBlack

	protected Map<String, Object> getRemommInfo(Connection con, String mobile,
			CstInfo c) {
		Map<String, Object> ret = new HashMap<String, Object>();
		ret.put("result", false);
		PreparedStatement queryPstmt = null;
		ResultSet queryRs = null;
		String table = CstUtil.calcCstSplitTable("prm_Agent_Cst_Recommed_",
				c.getCstGUID());
		String sql = "select CstMobile,CstName,r.ProjCode, p.ProjName from "
				+ table + " r left join p_Project p on r.ProjCode=p.ProjCode "
				+ " where r.CstGUID=? and r.enable=1 "
				+ " order by r.RecommTime desc limit 0, 1";

		try {
			queryPstmt = con.prepareStatement(sql);
			queryPstmt.setLong(1, c.getCstGUID());
			queryRs = queryPstmt.executeQuery();
			if (!queryRs.next()) {
				// 如果没记录，则直接返回
				ret.put("result", false);
				return ret;
			}
			ret.put("result", true);

			String cstMobile = queryRs.getString("CstMobile");
			String cstName = queryRs.getString("CstName");
			String projCode = queryRs.getString("ProjCode");
			String projName = queryRs.getString("ProjName");

			ret.put("CstMobile", cstMobile == null ? c.getMobile() : cstMobile);
			ret.put("cstName", cstName == null ? c.getCstName() : cstName);
			ret.put("projCode", projCode == null ? "" : projCode);
			ret.put("projName", projName == null ? "" : projName);
			return ret;
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			ADataSource.close(queryRs, queryPstmt, null);
		}

		return ret;
	} // 

	protected void persistent(ResultSet rs, String table) {
		Connection con = ADataSource.getConnection(ADataSource.SCHEMA_BIZ);
		try {
			String insertSql = CstUtil.formatInsertSql(table, C.FIELDS_CST);
			PreparedStatement pstmt = con.prepareStatement(insertSql);
			int columns = C.FIELDS_CST.split(",").length;
			for (int i = 1; i <= columns; i++) {
				pstmt.setString(i, rs.getString(i));
			}
			pstmt.executeUpdate();
			ADataSource.close(pstmt);
		} catch (SQLException e) {
			e.printStackTrace();
			bizLog.error("persistent error: " + e);
		} finally {
			ADataSource.close(con);
		}
	}

	protected CstInfo getByCardid(Connection con, String cardid) {
		String table = CstUtil.retrieveCardMapping(cardid);
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		try {
			CstInfo c = null;
			String sql = "select CstGUID from " + table + " where cardId=?";
			pstmt = con.prepareStatement(sql);
			pstmt.setString(1, cardid);
			rs = pstmt.executeQuery();
			if (rs.next()) {
				c = CstUtil.getByGuid(con, rs.getLong(1));
			}
			return c;
		} catch (Exception e) {
			e.printStackTrace();
			bizLog.error("get customer by cardid error: " + cardid);
			return null;
		} finally {
			ADataSource.close(rs, pstmt, null);
		}
	}

	protected CstInfo getByMobile(Connection con, String mobile) {
		if (mobile == null || mobile.length() < 11 || !Util.isMobile(mobile)) {
			return null;
		}
		String table = CstUtil.retrieveMobileMapping(mobile);
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		try {
			CstInfo c = null;
			String sql = "select CstGUID from " + table + " where mobile=?";
			pstmt = con.prepareStatement(sql);
			pstmt.setString(1, mobile);
			rs = pstmt.executeQuery();
			if (rs.next()) {
				c = CstUtil.getByGuid(con, rs.getLong(1));
			}
			return c;
		} catch (Exception e) {
			e.printStackTrace();
			bizLog.error("get customer by mobile error: " + mobile);
			return null;
		} finally {
			ADataSource.close(rs, pstmt, null);
		}
	}

	protected CstInfo getByOriGuid(Connection con, String oriGuid) {
		String table = CstUtil.retrieveGUIDMapping(oriGuid);
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		try {
			CstInfo c = null;
			String sql = "select CstGUID from " + table + " where OriGUID=?";
			pstmt = con.prepareStatement(sql);
			pstmt.setString(1, oriGuid);
			rs = pstmt.executeQuery();
			if (rs.next()) {
				c = CstUtil.getByGuid(con, rs.getLong(1));
			}
			return c;
		} catch (Exception e) {
			e.printStackTrace();
			bizLog.error("get customer by origuid error: " + oriGuid);
			return null;
		} finally {
			ADataSource.close(rs, pstmt, null);
		}
	}

	/**
	 * 保存客户信息
	 * 
	 * @throws TException
	 * @throws NumberFormatException
	 */
	protected long saveCstInfo(Connection con, CstInfo c,
			boolean saveMobileMapping) throws SQLException,
			NumberFormatException {
		if (saveMobileMapping) {
			bizLog.info("保存主档及所有映射[" + c + "]");
		} else {
			bizLog.info("保存主档及映射，不保存手机映射[" + c + "]");
		}
		long uid = 0;
		while(uid == 0) {
			pause = true;
			if (client == null) {
				bizLog.error("序列号服务未连接");
				connectSeriesService();
				sleep(1000);
				continue;
			}
			try {
				uid = Long.parseLong(client.getNextSerial("01"));
			} catch (TException ex) {
				if (client != null) {
					client.close();
				}
				connectSeriesService();
				bizLog.error("获取客户GUID序列号异常" + ex, ex);
				sleep(1000);
			}
		} // end for
		pause = false;
		

		c.setCstGUID(uid);
		String table = CstUtil.retrieveCustomerTable(c.getCstGUID());
		String fields = "CstGUID,"             //
				+ "CstName,"                   //
				+ "Gender,"                    //
				+ "BirthDate,"                 //
				+ "Language,"                  //
				+ "CstType,"                   //
				+ "CardType,"                  //
				+ "CardID,"                    //
				+ "MobileTel,"                 //
				+ "OfficeTel,"                 //
				+ "HomeTel,"                   //
				+ "Fax,"                       //
				+ "Email,"                     //
				+ "PostCode,"                  //
				+ "Address,"                   //
				+ "Country,"                   //
				+ "Province,"                  //
				+ "City,"                      //
				+ "Regional,"                  //
				+ "Road,"                      //
				+ "Nationality,"               //
				+ "NativePlace,"               //
				+ "SrcType,"                   //
				+ "LastModifyTime,"            //
				+ "OriGUID,"                   //
				+ "CreditEvaluate,"            //
				+ "CstCode,"                   //
				+ "Company,"                   //
				+ "Position,"                  //
				+ "Incoming,"                  //
				+ "WorkAddr,"                  //
				+ "CompanyTel,"                //
				+ "Race,"                      //
				+ "Religion,"                  //
				+ "Political,"                 //
				+ "IsMerried,"                 //
				+ "cogniChannel,`status`,"     //
				+ "CreateTime,CardID15,Level," //
				+ "`contact`,CstSrc";                 //
		String sql = CstUtil.formatInsertSql(table, fields);
		PreparedStatement pstmt = null;
		try {
			pstmt = con.prepareStatement(sql);
			int col = 1;
			pstmt.setLong(col++, c.getCstGUID()); // "CstGUID,"
			pstmt.setString(col++, c.getCstName()); // "CstName,"
			pstmt.setInt(col++, c.getGender()); // "Gender,"
			pstmt.setTimestamp(col++, c.getBirthday()); // "BirthDate,"
			pstmt.setString(col++, c.getLanguage()); // "Language,"
			pstmt.setInt(col++, c.getCstType());// "CstType,"
			pstmt.setInt(col++, c.getCardType());// "CardType,"
			pstmt.setString(col++, c.getCardId());// "CardID,"
			pstmt.setString(col++, c.getMobile());// "MobileTel,"
			pstmt.setString(col++, c.getOfficeTel());// "OfficeTel,"
			pstmt.setString(col++, c.getHomeTel());// "HomeTel,"
			pstmt.setString(col++, c.getFax());// "Fax,"
			pstmt.setString(col++, c.getEmail());// "Email,"
			pstmt.setString(col++, c.getPostCode());// "PostCode,"
			pstmt.setString(col++, c.getAddress());// "Address,"
			pstmt.setString(col++, c.getCountry());// "Country,"
			pstmt.setString(col++, c.getProvince());// "Province,"
			pstmt.setString(col++, c.getCity());// "City,"
			pstmt.setString(col++, c.getRegitonal());// "Regional,"
			pstmt.setString(col++, c.getRoad());// "Road,"
			pstmt.setString(col++, c.getNationality());// "Nationality,"
			pstmt.setString(col++, c.getNativePlace());// "NativePlace,"
			pstmt.setString(col++, c.getSrcType());// "SrcType,"
			pstmt.setTimestamp(col++, c.getLastModifyTime());// "LastModifyTime,"
			pstmt.setString(col++, c.getOriGUID());// "OriGUID,"
			pstmt.setString(col++, c.getCreditEvaluate());// "CreditEvaluate,"
			pstmt.setString(col++, c.getCstCode());// "CstCode,"
			pstmt.setString(col++, c.getCompany());// "Company,"
			pstmt.setString(col++, c.getPosition());// "Position,"
			pstmt.setDouble(col++, c.getIncoming());// "Incoming,"
			pstmt.setString(col++, c.getWorkAddr());// "WorkAddr,"
			pstmt.setString(col++, c.getCompanyTel());// "CompanyTel,"
			pstmt.setString(col++, c.getRace());// "Race,"
			pstmt.setString(col++, c.getReligion());// "Religion,"
			pstmt.setString(col++, c.getPolitical());// "Political,"
			pstmt.setInt(col++, 0); // "IsMerried,"
			pstmt.setString(col++, c.getCogniChannel()); // "cogniChannel,"
			pstmt.setInt(col++, c.getStatus());// "status,"
			pstmt.setTimestamp(col++, new Timestamp(System.currentTimeMillis())); // CreateTime
			pstmt.setString(col++, "");//c.getCardId15()); // CardID15
			pstmt.setInt(col++, c.getLevel());// "Level"
			pstmt.setString(col++, c.getContact());// "contact"
			pstmt.setString(col++, c.getCstSrc());// "CstSrc"
			pstmt.executeUpdate();

			if (saveMobileMapping) {
				// 手机映射
				saveMobileMapping(con, c, c.getMobile(), "");
			}
			// 身份证映射
			saveCardMapping(con, c);
			// GUID映射
			saveGUIDMapping(con, c);
			// 联名ID
			saveCstAccount(con, c);
			return uid;
		} catch (SQLException e) {
			e.printStackTrace();
			bizLog.error("save customer fail: " + e + "\n" + c);
			throw e;
		} finally {
			ADataSource.close(pstmt);
		}
		// log.info("saved: " + c.toString());
	} // ~saveCstInfo(CstInfo c)

	protected void saveMobileMapping(Connection con, CstInfo c, String mobile,
			String code) throws SQLException {
		// 手机映射
		bizLog.info(code + "保存手机映射[" + mobile + "-" + c.getCstGUID() + "]");
		if (!Util.isMobile(mobile)) {
			bizLog.info(code + "手机[" + mobile + "]无效，忽略保存");
			return;
		}
		String tb = CstUtil.retrieveMobileMapping(mobile);
		PreparedStatement pstmt = null;
		try {
			String sql = "insert " + tb + "(mobile,CstGUID)values(?,?)";
			bizLog.info(sql);
			pstmt = con.prepareStatement(sql);
			pstmt.setString(1, mobile);
			pstmt.setLong(2, c.getCstGUID());
			pstmt.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
			bizLog.error(code + "保存手机映射异常" + e);
			//			throw e;
		} finally {
			ADataSource.close(pstmt);
		}
	} // ~saveMobileMap(CstInfo c)

	protected void saveCardMapping(Connection con, CstInfo c)
			throws SQLException {
		String cardid = c.getCardId();
		if (!CardID.valid(cardid)) {
			return;
		}
		String tb = CstUtil.retrieveCardMapping(cardid);
		PreparedStatement pstmt = null;
		try {
			pstmt = con.prepareStatement("insert " + tb
					+ "(cardId,CstGUID)values(?,?)");
			pstmt.setString(1, cardid);
			pstmt.setLong(2, c.getCstGUID());
			pstmt.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
			bizLog.error("save cardid mapping fail: " + e + "\n" + c);
			throw e;
		} finally {
			ADataSource.close(pstmt);
		}
	} // ~saveCardMap(CstInfo c)

	protected void saveGUIDMapping(Connection con, CstInfo c)
			throws SQLException {
		String guid = c.getOriGUID();
		long uid = c.getCstGUID();
		String tb = CstUtil.retrieveGUIDMapping(guid);
		PreparedStatement pstmt = null;
		try {
			pstmt = con.prepareStatement("insert " + tb
					+ "(OriGUID,CstGUID)values(?,?)");
			pstmt.setString(1, guid);
			pstmt.setLong(2, uid);
			pstmt.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
			bizLog.error("save guid mapping fail: " + e + "\n" + c);
			//			throw e;
		} finally {
			ADataSource.close(pstmt);
		}
	} // ~saveCardMap(CstInfo c)

	protected void saveCstAccount(Connection con, CstInfo c)
			throws SQLException {
		long uid = c.getCstGUID();
		PreparedStatement pstmt = null;
		try {
			pstmt = con
					.prepareStatement("insert cst_Account(CstGUID,RealAccount)values(?,?)");
			pstmt.setLong(1, uid);
			pstmt.setLong(2, uid);
			pstmt.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
			bizLog.error("save cst account fail: " + e + "\n" + c);
		} finally {
			ADataSource.close(pstmt);
		}
	} // ~saveCardMap(CstInfo c)

	protected void deleteMobileMapping(Connection con, String mobile)
			throws SQLException {
		if (!Util.isMobile(mobile)) {
			bizLog.info(mobile + " is invalid");
			return;
		}
		String tb = CstUtil.retrieveMobileMapping(mobile);
		PreparedStatement pstmt = null;
		try {
			pstmt = con.prepareStatement("delete from " + tb
					+ " where mobile=?");
			pstmt.setString(1, mobile);
			pstmt.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
			bizLog.error("delete mobile mapping fail: " + e + "\n" + mobile);
		} finally {
			ADataSource.close(pstmt);
		}
	} //~deleteMobileMapping

	protected void updateMobileMapping(Connection con, String mobile,
			long from, long toUid, String code) throws SQLException {
		bizLog.info(code + "更新手机映射[" + mobile + "-" + from + "]->[" + mobile
				+ "-" + toUid + "]");
		if (!Util.isMobile(mobile)) {
			bizLog.info(code + "手机[" + mobile + "]无效，忽略更新");
			return;
		}
		String tb = CstUtil.retrieveMobileMapping(mobile);
		PreparedStatement pstmt = null;
		try {
			String sql = "update " + tb + " set CstGUID = ? where mobile=?";
			pstmt = con.prepareStatement(sql);
			bizLog.info(sql);
			pstmt.setLong(1, toUid);
			pstmt.setString(2, mobile);
			pstmt.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
			bizLog.error(code + "更新手机映射异常" + e);
		} finally {
			ADataSource.close(pstmt);
		}
	}

	protected void updateCstInfo(Connection con, CstInfo c, CstInfo old)
			throws SQLException {
		if (c == null || c.getCstGUID() == 0) {
			bizLog.warn("invalid cstinfo: " + c);
			return;
		}
		String table = CstUtil.retrieveCustomerTable(c.getCstGUID());
		String fields = ""
				+ "CstName,"
				+ "Gender,"
				+ "BirthDate,"
				+ "Language,"
				+ "CstType,"
				+ "CardType,"
				+ "CardID,"
				+ "MobileTel,"
				+ "OfficeTel,"
				+ "HomeTel,"
				+ "Fax,"
				+ "Email,"
				+ "PostCode,"
				+ "Address,"
				+ "Country,"
				+ "Province,"
				+ "City,"
				+ "Regional,"
				+ "Road,"
				+ "Nationality,"
				+ "NativePlace,"
				+ "SrcType,"
				+ "LastModifyTime,"
				+ "OriGUID,"
				+ "CreditEvaluate,"
				+ "CstCode,"
				+ "Company,"
				+ "Position,"
				+ "Incoming,"
				+ "WorkAddr,"
				+ "CompanyTel,"
				+ "Race,"
				+ "Religion,"
				+ "Political,IsMerried,cogniChannel,status,CardID15,Level,`contact`";
		String sql = CstUtil.formatUpdateSql(table, fields)
				+ " where CstGUID = ?";
		PreparedStatement pstmt = null;
		try {
			String ccstName = c.getCstName();
			if (ccstName == null || "".equals(ccstName)) {
				c.setCstName(old.getCstName());
			}
			String ccardId = c.getCardId();
			if (ccardId == null || "".equals(ccardId)) {
				c.setCardId(old.getCardId());
			}
			String cmobile = c.getMobile();
			if (cmobile == null || "".equals(cmobile)) {
				c.setMobile(old.getMobile());
			}
			String cofficeTel = c.getOfficeTel();
			if (cofficeTel == null || "".equals(cofficeTel)) {
				c.setOfficeTel(old.getOfficeTel());
			}
			String chomeTel = c.getHomeTel();
			if (chomeTel == null || "".equals(chomeTel)) {
				c.setHomeTel(old.getHomeTel());
			}
			String caddr = c.getAddress();
			if (caddr == null || "".equals(caddr)) {
				c.setAddress(old.getAddress());
			}

			pstmt = con.prepareStatement(sql);
			int col = 1;
			//pstmt.setLong(col++, c.getCstGUID()); // "CstGUID,"
			pstmt.setString(col++, c.getCstName()); // "CstName,"
			pstmt.setInt(col++, c.getGender()); // "Gender,"
			pstmt.setTimestamp(col++, c.getBirthday()); // "BirthDate,"
			pstmt.setString(col++, c.getLanguage()); // "Language,"
			pstmt.setInt(col++, c.getCstType());// "CstType,"
			pstmt.setInt(col++, c.getCardType());// "CardType,"
			pstmt.setString(col++, c.getCardId());// "CardID,"
			pstmt.setString(col++, c.getMobile());// "MobileTel,"
			pstmt.setString(col++, c.getOfficeTel());// "OfficeTel,"
			pstmt.setString(col++, c.getHomeTel());// "HomeTel,"
			pstmt.setString(col++, c.getFax());// "Fax,"
			pstmt.setString(col++, c.getEmail());// "Email,"
			pstmt.setString(col++, c.getPostCode());// "PostCode,"
			pstmt.setString(col++, c.getAddress());// "Address,"
			pstmt.setString(col++, c.getCountry());// "Country,"
			pstmt.setString(col++, c.getProvince());// "Province,"
			pstmt.setString(col++, c.getCity());// "City,"
			pstmt.setString(col++, c.getRegitonal());// "Regional,"
			pstmt.setString(col++, c.getRoad());// "Road,"
			pstmt.setString(col++, c.getNationality());// "Nationality,"
			pstmt.setString(col++, c.getNativePlace());// "NativePlace,"
			pstmt.setString(col++, c.getSrcType());// "SrcType,"
			pstmt.setTimestamp(col++, c.getLastModifyTime());// "LastModifyTime,"
			pstmt.setString(col++, c.getOriGUID());// "OriGUID,"
			pstmt.setString(col++, c.getCreditEvaluate());// "CreditEvaluate,"
			pstmt.setString(col++, c.getCstCode());// "CstCode,"
			pstmt.setString(col++, c.getCompany());// "Company,"
			pstmt.setString(col++, c.getPosition());// "Position,"
			pstmt.setDouble(col++, c.getIncoming());// "Incoming,"
			pstmt.setString(col++, c.getWorkAddr());// "WorkAddr,"
			pstmt.setString(col++, c.getCompanyTel());// "CompanyTel,"
			pstmt.setString(col++, c.getRace());// "Race,"
			pstmt.setString(col++, c.getReligion());// "Religion,"
			pstmt.setString(col++, c.getPolitical());// "Political,"
			pstmt.setInt(col++, 0); // "IsMerried,"
			pstmt.setString(col++, c.getCogniChannel()); // "cogniChannel,"
			pstmt.setInt(col++, c.getStatus());// "status,"
			//			pstmt.setTimestamp(col++, c.getCreateTime()); // CreateTime
			pstmt.setString(col++, "");//c.getCardId15()); // CardID15
			pstmt.setInt(col++, c.getLevel());// "Level"
			pstmt.setString(col++, c.getContact());// "contact"
			pstmt.setLong(col++, c.getCstGUID());
			pstmt.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
			bizLog.error("update customer info fail: " + e + "\n" + c);
			throw e;
		} finally {
			ADataSource.close(pstmt);
		}
	}
} ///~CustomerSync